package mop.model.task;

import java.beans.PropertyChangeEvent;
import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;

import mop.exception.BusinessRule1Exception;
import mop.exception.BusinessRule2Exception;
import mop.exception.BusinessRule3Exception;
import mop.exception.IllegalDependencyException;
import mop.exception.IllegalDuplicationException;
import mop.exception.IllegalIdException;
import mop.exception.IllegalValueException;
import mop.exception.RecursiveBusinessRule2Exception;
import mop.model.AbstractModel;
import mop.model.ModelInterface;
import mop.model.project.Project;
import mop.model.resource.Resource;
import mop.model.resource.ResourceInterface;
import mop.model.task.field.Field;
import mop.model.task.state.State;
import mop.model.task.state.StateFactory;
import mop.model.task.state.Successful;
import mop.model.task.state.Unfinished;
import mop.model.task.tasktype.TaskType;
import mop.model.task.tasktype.TaskTypeInterface;
import mop.model.user.User;
import mop.util.Memento;

/**
 * Class for Task.
 * 
 * @invar 	Identifier is valid
 * 			|isValidIdentifier(getId())
 * 
 * @invar 	Has a schedule
 *  		|getSchedule() !=  null
 *  
 * @invar 	Dependencies are valid
 *  		|isValidDependencies(this,getDependencies())
 * 
 * @invar	Business rule 2 is met
 * 			|getStatus().rule2isMet() == true
 * 
 * @invar	Business rule 3 is met
 * 			|getStatus().rule3isMet() == true
 * 
 * @author 	MOP Team 7
 */

public class Task extends AbstractModel implements TaskInterface,Memento<TaskMemento>
{
	/**
	 * Constructor for Task
	 * 
	 * @param id			The task identifier
	 * @param tt			The TaskType
	 * @param start			The start time for this task
	 * @param dur			The duration it will take to complete this task
	 * @param deadline		The deadline for this task
	 * @param dependencies	The dependencies this task has
	 * @param resources		The resources this task might use
	 * @param user			The user to which this task belongs
	 * 
	 * @throws IllegalIdException
	 *	|  !isValidId(id)
	 * @throws IllegalDeadlineException
	 * 	| @see Schedule constructor
	 * @throws IllegalDependencyException
	 * 	| !isValidDependencies(dependencies)
	 * @throws IllegalDuplicationException 
	 * 	| @see User#addTask(Task)
	 * @throws BusinessRule1Exception 
	 * 	| @see Schedule#setDates(Timestamp, Timestamp, Timestamp)
	 * @throws BusinessRule3Exception 
	 *  | @see Schedule#setDates(Timestamp, Timestamp, Timestamp)
	 * 
	 * @post set new identifier
	 * 	| this.getId() == id
	 * 
	 * @effect set new schedule
	 * 	| this.setSchedule
	 * @effect set Dependencies
	 * 	| this.setDependencies
	 * @effect set Resources
	 * 	| this.setResources
	 * @effect register task with user
	 * 	| user.addTask(this) @see User
	 * @effect the status is set to Available/Unavailable
	 *  | StateFactory.createUncheckedState
	 * @effect Property change support is initialized
	 * 	| this.propertyChangeSupport = new PropertyChangeSupport(this)
	 */
	public Task(String id, TaskType tt,Timestamp start,Timestamp dur,Timestamp deadline,List<Task> dependencies,List<Resource> resources,User user) throws IllegalDependencyException, IllegalDuplicationException, BusinessRule3Exception, BusinessRule1Exception, IllegalIdException 
	{	
		try
		{
			if(!isValidId(id))
			{
				throw new IllegalIdException(id);
			}
			this.id = id;
			
			this.setUser(user);
			this.tasktype = tt;
			setFields(this.tasktype);
			
			/** create and register new schedule **/
			this.setDependencies(dependencies);
			Schedule sched = new Schedule(this,start,dur,deadline);
			this.setSchedule(sched);
		
			this.setResources(resources);
		
			
			/**
			 * Despite the name this will return a state that is either
			 * Available or Unavailable depending on what's in this
			 * Unchecked just doesn't care about the exceptions
			 */
			this.status = StateFactory.createUncheckedState(Unfinished.stateName(),this);
		}
		catch(IllegalDependencyException ec)
		{
			this.setUser(null);
			throw ec;
		}
		catch(IllegalDuplicationException ed)
		{
			this.setUser(null);
			throw ed;
		}
		catch(BusinessRule3Exception ef)
		{
			this.remove(0);
			throw ef;
		}
		catch(BusinessRule1Exception eg)
		{
			this.remove(0);
			throw eg;
		}
	}
	
	/**
	 * Modify task details
	 * 
	 * @param start			The new start time for this task
	 * @param dur			The new duration it will take to complete this task
	 * @param deadline		The new deadline for this task
	 * @param dependencies	The new dependencies this task has
	 * @param resources		The new resources this task might use
	 * 
	 * @throws IllegalDeadlineException
	 * 	| @see Schedule constructor
	 * @throws IllegalDependencyException
	 * 	| !isValidDependencies(dependencies)
	 * @throws IllegalDuplicationException 	If a similar task already exists
	 * 	| getUser().getTasks().contains(this)
	 * @throws BusinessRule1Exception 
	 * 	| @see Schedule#setDates(Timestamp, Timestamp, Timestamp)
	 * @throws RecursiveBusinessRule2Exception
	 * @throws BusinessRule3Exception 
	 *  | @see Schedule#setDates(Timestamp, Timestamp, Timestamp)
	 * 
	 * @effect set new schedule
	 * 	| this.setSchedule
	 * @effect set Dependencies
	 * 	| this.setDependencies
	 * @effect set Resources
	 * 	| this.setResources
	 */	
	public void modifyDetails(Timestamp start,Timestamp dur,Timestamp deadline,List<Task> dependencies,List<Resource> resources) throws IllegalDependencyException, IllegalDuplicationException, BusinessRule3Exception, BusinessRule1Exception, BusinessRule2Exception, RecursiveBusinessRule2Exception 
	{
		this.setDependencies(dependencies);
		this.getSchedule().setDates(start, dur, deadline);
		this.setResources(resources);
		this.updateStatus(this.forceGetStatus().toString());
	}
	/***************************************************************************
	 * 						Model interface
	 ***************************************************************************/

	
	public TaskInterface getInterface()
	{
		return this;
	}
	
	
	/***************************************************************************
	 * 							Task identifier
	 ***************************************************************************/
	
	
	/**
	 * @see ModelInterface#getId()
	 */
	public String getId() 
	{
		return id;
	}
	
	 /**
	  * Checks if the passed identifier is valid
	  * @param id	The identifier to check
	  * @return true if the passed id is not empty and its not null
	  *  |((!(id().equals("")) && id != null))
	  */
	 public static boolean isValidId(String id)
	 {
		 return ((!(id.trim().equals("")) && id != null));
	 }
	
	
	 /**
	  * variable for the id of the project
	  */
	private final String id;

	/***************************************************************************
	 * 							Task Fields
	 ***************************************************************************/
	
	/**
	 * Clone the fields of tasktype
	 * @post fields are set
	 */
	private void setFields(TaskType tt)
	{
		fields = tt.getFields();
	}
	
	/**
	 * Change the field
	 * 
	 * TODO what if not found ?
	 * 
	 * @param Identifier the identifier you want to change
	 * @param value the value you want to set
	 * 
	 * @throws IllegalValueException
	 * 	| when value is illegal
	 */
	public void changeField(String Identifier,String value) throws IllegalValueException
	{
		for(Field f : fields)
		{
			if(f.getDescription().equals(Identifier))
			{
				f.setValue(value);
			}
		}
	}
	
	/**
	 * Fetch the field
	 * 
	 * @param Identifier the identifier of the field
	 * 
	 * @return the value of a field
	 */
	public String getValue(String Identifier)
	{
		for(Field f : fields)
		{
			if(f.getDescription().equals(Identifier))
			{
				if(f.getValue() == null) return "(null)";
				else return f.getValue();
			}
		}
		return null;
	}
	
	/**
	 * Get Fields
	 * 
	 * @return a clone of the fields list
	 */
	public List<Field> getFields()
	{
		if(this.fields == null) return null;
		LinkedList<Field> ret = new LinkedList<Field>();
		for(Field a : fields)
		{
			Field b = a.clone();
			ret.add(b);
		}
		return ret;
	}
	
	/** list containing all fields */
	private List<Field> fields = null;
	
	/***************************************************************************
	 * 							Task dependencies
	 ***************************************************************************/
	
	/**
	 * @see TaskInterface#getDependencyList()
	 */
	public List<TaskInterface> getDependencyList()
	{
		List<TaskInterface> d = new LinkedList<TaskInterface>();
		d.addAll(dependencies);
		return d;
	}
	
	/**
	 * Return a copy of the list with dependencies
	 * 
	 * @return dependencies
	 */
	List<Task> getDependencies()
	{
		List<Task> d = new LinkedList<Task>();
		d.addAll(dependencies);
		return d;
	}
	

	/**
	 * Set the dependencies to given list, this doesn't check consistency with business rule 1
	 * 
	 * @effect Removes deleted dependencies
	 * 	|removeDepencies(dep)
	 * @effect Add new dependencies
	 * 	|addDependencies(dep)
	 * 
	 * @throws IllegalDependencyException
	 * 	If dependencies are not valid
	 * 		|!isValidDependencies(this,dep)
	 */
	private void setDependencies(List<Task> dep) throws IllegalDependencyException 
	{
		if(!isValidDependencies(this,dep))
			throw new IllegalDependencyException();
		removeDependencies(dep);
		addDependencies(dep);
	}
	
	
	/**
	 * Set the dependencies to given list, this checks consistency with business rule 1
	 * @see setDependencies(List<Task>)
	 * @throws BusinessRule1Exception
	 * 	| @see Schedule#hasValidDeadline()
	 */
	public void trySetDependencies(List<Task> dep) throws IllegalDependencyException, BusinessRule1Exception
	{
		if(!Schedule.isValidDeadline(this.getSchedule().getStart(),this.getSchedule().getDuration(),this.getSchedule().getDeadline(),dep,this.getAllParents()))
			throw new BusinessRule1Exception();
		setDependencies(dep);
	}
	
	/**
	 * Check whether given task can use given task list as dependencies without creating a loop
	 * this is the reverse of dependsOn
	 * 
	 * @see dependsOn(t,dep)
	 * 
	 * @return 
	 * 	false if this configuration would create a loop
	 * 	true otherwise
	 */
	public static boolean isValidDependencies(Task t, List<Task> dep)
	{
		if(dependsOn(t,dep))
			return false;
		return true;
	}
	
	/**
	 * Checks whether given task is a direct or in-direct dependency of this task
	 * 
	 * @see dependsOn(t,getDependencies())
	 */
	private boolean dependsOn(Task t)
	{
		return dependsOn(t,getDependencies());
	}
	
	/**
	 * Checks whether a given task belongs to a given dependency list
	 * 
	 * @param t		The task to check
	 * @param dep	The dependency list
	 * 
	 * @return 
	 * 	true if task direct or indirect belongs to dep
	 * 	false otherwise
	 */
	private static boolean dependsOn(Task t, List<Task> dep)
	{
		for(Task d : dep)
		{
			if(d.equals(t) || d.dependsOn(t))
				return true;
		}
		return false;
	}
	
	
	/**
	 * Removes the current dependencies that are not in the given list
	 * 
	 * @param dep 	List with new dependencies
	 * 
	 * @post All task dependencies are in the given dep list
	 * 	|for i = 0 ... getDependencies().size() - 1
	 * 	|	dep.contains(getDependencies().get(i)) == true
	 */
	private void removeDependencies(List<Task> dep)
	{
		for(Task t : getDependencies())
		{
			if(!dep.contains(t))
				this.removeDependency(t);
		}
	}
	
	/**
	 * Removes given dependency and updates given task
	 * 
	 * @param t	The dependency to be removed from the task dependencies
	 * 
	 * @effect remove this task from the given task's parent list to maintain integrity
	 * 	|t.removeDependsOnMe(this)
	 * @effect remove t from dependencies
	 * 	|getDependencies().remove(t)
	 */
	private void removeDependency(Task t){
		t.removeParent(this);
		dependencies.remove(t);
	}
	
	/**
	 * Adds dependencies from given list that are not dependencies of this task yet
	 * 
	 * @param dep 	List with new dependencies
	 * 
	 * @post All dependencies from given dep list are in the task dependencies list
	 * 	|for i = 0 ... dep.size() - 1
	 * 	|	getDependencies().contains(dep.get(i)) == true
	 */
	private void addDependencies(List<Task> dep)
	{
		for(Task t : dep)
		{
			if(!dependencies.contains(t))
				addDependency(t);
		}
	}
	
	/**
	 * Adds given dependency and updates given task
	 * 
	 * @param t	The dependency to be added to the task dependencies
	 * 
	 * @effect add this task to the parents list of t to maintain integrity
	 * 	|t.addDependsOnMe(this)
	 * @effect add t to the dependencies list
	 * 	|getDependencies().add(t)
	 */
	private void addDependency(Task t)
	{
		t.addParent(this);
		dependencies.add(t);
	}

	/**
	 * List containing task dependencies
	 */
	private List<Task> dependencies = new LinkedList<Task>();
	
	
	/***************************************************************************
	 * 				Task Parents (task depending on this task)
	 ***************************************************************************/
	/**
	 * @see TaskInterface#getParentList()
	 */
	public List<TaskInterface> getParentList()
	{
		List<TaskInterface> p = new LinkedList<TaskInterface>();
		p.addAll(parents);
		return p;
	}
	
	/**
	 * Return a copy of the list with tasks that directly depend on this task
	 * 
	 * @return parents
	 */
	List<Task> getParents()
	{
		List<Task> p = new LinkedList<Task>();
		if(parents != null)
			p.addAll(parents);
		return p;
	}
	
	/**
	 * Add task that depends on this task
	 * 
	 * @param t	Task to add
	 * 
	 * @effect add task that depends on this task
	 * 	|getParents().add(t);
	 */
	private void addParent(Task t)
	{
		parents.add(t);
	}
	
	/**
	 * Remove task that depends on this task
	 * 
	 * @param t	Task to remove
	 * 
	 * @effect removes task that depends on this task
	 * 	|getParents().remove(t);
	 * @effect remove this from the dependency list of the parent
	 * | parent.remoceDependency(t)
	 */
	private void removeParent(Task t)
	{
		parents.remove(t);
	}
	
	/**
	 * @see TaskInterface#getAllParentList()
	 */
	public List<TaskInterface> getAllParentList()
	{
		List<TaskInterface> list = new LinkedList<TaskInterface>();
		list.addAll(getAllParents());
		return list;
	}
	
	/**
	 * Get a copy of the list with all tasks that depend directly or indirectly on this task
	 * @return List with all direct and indirect parents
	 */
	public List<Task> getAllParents()
	{
		List<Task> list = new LinkedList<Task>();
		getAllParents(list);
		return list;
	}
	
	/**
	 * Add all tasks that depend directly or indirectly on this task to given list
	 * @param list	List to add parents to
	 */
	private void getAllParents(List<Task> list)
	{
		for(Task p : getParents())
		{
			list.add(p);
			p.getAllParents(list);
		}
	} 


	/**
	 * Are all dependencies successful ?
	 * @return if a dependency is unsuccessful return false
	 */
	public boolean dependenciesAreSuccesfull()
	{
		for(Task t : this.getDependencies())
		{
			if(!t.toString().equals(Successful.stateName()))
			{
				return false;
			}
		}
		return true;
	}
	
	/**
	 * List contain tasks that depend on this task (have this task as dependency)
	 */
	private List<Task> parents = new LinkedList<Task>();

	
	/***************************************************************************
	 * 						Schedule
	 ***************************************************************************/
	
	/**
	 * @see TaskInterface#getSchedule()
	 */
	public Schedule getSchedule()
	{
		return this.schedule;
	}
	
	
	/**
	 * Mutator for schedule member
	 * 
	 * @param schedule
	 *  the new schedule instance for this task
	 */
	private void setSchedule(Schedule schedule)
	{
		this.schedule = schedule;
	}
	
	/**
	 * the Schedule that is used for the task
	 */
	private Schedule schedule;
	
	
	/***************************************************************************
	 * 							Task resources
	 ***************************************************************************/
	
	/**
	 * All resources the task needs are available
	 * @warn changed to be passed to TaskType !!
	 * @warn INFORMATION EXPERT 	
	 */
	public boolean allResourcesAvailable()
	{
		return this.tasktype.resourceConstraints(this);
	}
	
	/**
	 * Return list with resources
	 * @return resources
	 */
	public List<Resource> getResources()
	{	
		List<Resource> r = new LinkedList<Resource>();
		if(resources != null)
			r.addAll(resources);
		return r;
	}
	
	/**
	 * @see TaskInterface#getResourcesList()()
	 */
	public List<ResourceInterface> getResourcesList()
	{
		List<ResourceInterface> r = new LinkedList<ResourceInterface>();
		if(resources != null)
			r.addAll(resources);
		return r;
	}
	
	/**
	 * Set the resources to given list
	 * 
	 * @param res	List with resources
	 * 
	 * @effect Removes deleted resources
	 * 	|removeResources(res);
	 * @effect Add new resources
	 * 	|addResources(res);
	 */
	public void setResources(List<Resource> res)
	{
		removeResources(res);
		addResources(res);
	}
	
	/**
	 * Removes the current resources that are not in the given list
	 * 
	 * @param res	List with new resources
	 * 
	 * @post All task resources are in the given res list
	 * 	|for i = 0 ... getResources().size() - 1
	 * 	|	res.contains(getResources().get(i)) == true
	 */
	private void removeResources(List<Resource> res)
	{
		for(Resource r : getResources())
		{
			if(!res.contains(r))
				removeResource(r);
		}
	}
	
	/**
	 * Removes given resource and updates given resource with task
	 * 
	 * @param t	The resource to be removed from the task resources list
	 * 
	 * @effect remove this task from the given resource 'tasksRequiring' list to maintain integrity
	 * 	|getTasksRequiring().remove(this)
	 * @effect remove r from resources
	 * 	|getResources().remove(r)
	 */
	private void removeResource(Resource r)
	{
		r.getTasksRequiring().remove(this);
		resources.remove(r);
	}
	
	/**
	 * Adds resources from given list that are not resources of this task yet
	 * 
	 * @param dep 	List with new resources
	 * 
	 * @post All resources from given res list are in the task resources list
	 * 	|for i = 0 ... res.size() - 1
	 * 	|	getResources().contains(res.get(i)) == true
	 */
	private void addResources(List<Resource> res)
	{
		for(Resource r : res)
		{
			if(!getResources().contains(r))
				addResource(r);
		}
	}
	
	/**
	 * Adds given resource and updates given resource with task
	 * 
	 * @param r	The resource to be added to the task resources
	 * 
	 * @effect Add this task to the given resource 'tasksRequiring' list to maintain integrity
	 * 	|r.getTasksRequiring().add(this)
	 * 
	 * @effect Add r to resources
	 * 	|getResources().add(r);
	 */
	private void addResource(Resource r)
	{
		r.getTasksRequiring().add(this);
		resources.add(r);
	}

	
	/**
	 * List containing task resources
	 */
	private List<Resource> resources = new LinkedList<Resource>();
	
	/***************************************************************************
	 * 						State
	 ***************************************************************************/
	
	/**
	 * Update Status
	 * UseCase 4.2.3
	 * change status to passed proposition
	 * 
	 * @param status	Status to set
	 * 
	 * @throws BusinessRule3Exception 
	 * 		| alternateflow 2
	 * 		| @see StateFactory
	 * 
	 * @throws BusinessRule2Exception 
	 * 		| alternateflow 1
	 * 		| @see StateFactory
	 * @throws RecursiveBusinessRule2Exception 
	 *
	 * @post status is set to given status
	 * 	|getStatus() == StateFactory.createState(status,this)
	 */
	public void updateStatus(String status) throws BusinessRule2Exception, BusinessRule3Exception, RecursiveBusinessRule2Exception
	{
                String oldStatus = this.getStatus().getName();
		this.status = StateFactory.createUpdatedState(status,this.getStatus());
		propertyChangeSupport.firePropertyChange(new PropertyChangeEvent(this.getInterface(), "status", oldStatus, status));
	}
	
	/**
	 * Force a status, without checking business rules
	 * @param status	Status to set
	 * @post Status is set to given status
	 * 	|forceGetStatus() == StateFactory.createUncheckedState(status,this)
	 */
	public void forceStatus(String status)
	{
                String oldStatus = this.getStatus().getName();
		this.status = StateFactory.createUncheckedState(status,this);
		propertyChangeSupport.firePropertyChange(new PropertyChangeEvent(this.getInterface(), "status", oldStatus, status));
	}
	
	
	/**
	 * @see TaskInterface#getStatus()
	 * @effect this.status.refreshStatus();
	 */
	public State getStatus() 
	{
		this.status.refreshStatus();
		return this.status;
	}
	
	/**
	 * Get Status without refreshing
	 */
	public State forceGetStatus()
	{
		return this.status;
	}
	
	/**
	 * the state of the task
	 */
	private State status;
	
	
	/***************************************************************************
	 * 						User for this task
	 ***************************************************************************/
	
	/**
	 * Set the user for this task
	 * @param	user	The user who owns this task
	 * 
	 * @throws IllegalDuplicationException
	 * 	| @see User#addTask(Task)
	 * 
	 * @post User is set to given value
	 * 	|getUser() == user
	 * 
	 * @effect If the task used to belong to another user, un-bind the task with that user
	 * 	|getUser().removeTask(this)
	 * 
	 * @effect Assign task to user
	 * 	|user.addTask(this)
	 */
	private void setUser(User user) throws IllegalDuplicationException{
		if(getUser() != null)
			getUser().removeTask(this);
		
		this.user = user;
		if(user != null)
			user.addTask(this);
	}
	
	/**
	 * @see TaskInterface#getUser()
	 */
	public User getUser(){
		return this.user;
	}
	

	/**
	 * User Owner constraints
	 * @warn passed to TaskType
	 */
	public boolean isOwnerType()
	{
		return this.tasktype.userConstraints(this.getUser());
	}
	
	/**
	 * the User who owns this task
	 */
	private User user;
	
	/***************************************************************************
	 * 							Task project
	 ***************************************************************************/
	
	/**
	 * Sets the project to given value
	 * 
	 * @param p Project
	 * 
	 * @post project equals given value
	 * 	|getProject() == p
	 */
	public void setProject(Project p)
	{
		if(getProject() != null)
			getProject().removeTask(this);
		
		this.project = p;
		if(p != null)
			p.addTask(this);
	}
	
	/**
	 * @see TaskInterface#getProject()
	 */
	public Project getProject()
	{
		return project;
	}
	
	/**
	 * Variable to indicate to which project the task belongs
	 */
	private Project project = null;


	/***************************************************************************
	 * 							Remove task
	 ***************************************************************************/
	

	/**
	 * Remove this task
	 * @param depth The depth to remove in
	 * 	|0 = just this task
	 * 	|1 = this task + direct parents
	 * 	|2 = this task + all parents recursive
	 * 
     * @effect	If depth == 2, remove all recursive parents
     * @effect  If depth == 1, remove all direct parents
     * @effect	Un-bind with all dependencies
     * @effect	Un-bind with all parents
     * @effect	Un-bind with all resources
     * @effect	Un-bind with the user
     * @effect	Un-bind with the project
     * @effect	Un-bind with schedule
     * @post	Un-binded with status
     * 	|this.status = null
	 */
	public void remove(int depth)
	{	
		if(depth < 0 || depth > 2)
			throw new IllegalArgumentException("Depth can only be between 0 and 2");
		
		List<Task> p;	//Select other tasks to delete
		if(depth == 2)
			p = getAllParents();
		else if(depth == 1)
			p = getParents();
		else
			p = new LinkedList<Task>();
		
		for(Task t : p){ //remove parents according to depth
			t.remove(0);
		}

		for(Task t : getDependencies()){	//un-bind with dependencies
			this.removeDependency(t);
		}
		for(Task t : getParents()){		//un-bind with parents
			t.removeDependency(this);
		}
	
		for(Resource r : getResources()){	//un-bind with resources
			removeResource(r);
		}

		setProject(null); //un-bind with project
		
		try {
			setUser(null); //un-bind with user
		} catch (IllegalDuplicationException e) {
			//cannot happen as we set the user to null
		} 

		this.status = null; //un-bind with status
		setSchedule(null); //un-bind with schedule
		
	}

	/***************************************************************************
	 * 						TaskType support
	 ***************************************************************************/
	
	/**
	 * Inspector for TaskType
	 * 
	 * @return returns an interface to the tasktype
	 */
	public TaskTypeInterface getTaskType()
	{
		return this.tasktype.getInterface();
	}
	
	
	/** the tasktype of the task */
	private final TaskType tasktype;
	
	/***************************************************************************
	 * 						Java object interface
	 ***************************************************************************/
	
	/**
	 * @see Object#equals(Object)
	 */
	public boolean equals(Object obj)
	{
		if(obj.getClass() == getClass())
		{
			if(((Task) obj).getId().equals(this.getId()))
				return true;
		}
		return false;
	}
	
	/**
	 * @see Object#toString()
	 */
	public String toString()
	{
		return getId();
	}
	
	
	/***************************************************************************
	 * 						Memento pattern Support
	 ***************************************************************************/
	
	/**
	 * Save current Task instance to a memento
	 * @return	Memento with current state
	 */
    public TaskMemento saveMemento() {
        return new TaskMemento(getId(),getUser(),getDependencies(),getParents(),forceGetStatus(),getSchedule(),getResources());
    }
 
    /**
     * Restore to a given memento
     * @param m	Memento to restore to
     * @effect	All attributes are set to original value
     */
    public void restoreMemento(TaskMemento memento) {

        try {
			setUser(memento.getUser());
		} catch (IllegalDuplicationException e1) {
			// can't happen as this used to be valid
		}
		
        this.getSchedule().restoreMemento(memento.getSchedule());
        
        try {
			setDependencies(memento.getDependencies());
		} catch (Throwable e) {
			//Can't happen as the dependencies used to be valid in this context
		}
        this.parents = memento.getParents();
        this.status = memento.getStatus();
        setResources(memento.getResources());
    }
}
